package com.archgeek.java.conc.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 同一时刻只允许一个线程占有锁
 *
 * @author pizhihui
 * @date 2023-04-20 15:41
 */
public class Mutext implements Lock {

    private static class Sync extends AbstractQueuedSynchronizer {
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
    }

    private final Sync sync = new Sync();

    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        // TODO return sy;
        return null;
    }


    public static void main(String[] args) {

        Lock lock = new Mutext();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                lock.lock();
                try {
                    // 需要设置一个随机休眠时间来验证结果，线程是每200毫秒创建一个，
                    // 如果每个线程都休眠相同的时间，先创建的线程肯定会先执行完的，就算不加公平锁，结果仍然是顺序输出
                    int randomNumber = (int) Math.ceil(Math.random() * 1000);
                    Thread.sleep(randomNumber);
                    System.out.println(String.format("Thread %s Completed", Thread.currentThread().getName()));
                } catch (Exception e) {

                } finally {
                    lock.unlock();

                }
            }).start();
        }

    }

}
